# -*- encoding: utf-8 -*-
import json
import urllib2
import getopt
import traceback
import sys
import re
import time
from xml.etree import ElementTree
reload(sys)
sys.setdefaultencoding("utf-8")

SITE='--site'
PROXY='--proxyConf'
DEVTYPE='--devType'
VERSIONLIST='--versionList'

EXCEPTION=10000
URLERROR=10001
HTTPERROR=10002
BADSTATUS=10003

def usage():
    Useage='''Useage:
    python get_dev_patch_list.py --site "upgradeSite" --proxyConf "" --devType "" --versionList ""
        --site:       the upgrade site, eg: http://update.epic.com/update/
        --proxyConf   the proxy config, eg:{"pswd": "pswd", "user": "user","server": "1.1.1.1","port":806}
        --devType     the device's type, eg:IPS
        --versionList the device's all version info, eg:
        [
            {"vtype":"firmwire","version":"5.6.9.31"},
            {"vtype":"agent","version":"5.6.9.63"},
            {"vtype":"sysrule","version":"5.6.9.12581"},
            {"vtype":"ulrlib","version":"5.6.0.130"},
            {"vtype":"avrule","version":"5.6.9.12542"}
        ]
        return: if call failed return the [], else return the device's patchList, eg:
        [
            {
                "devType":"RSAS",
                "patchType":"vulsys",
                "name":"rsas-sysvul-version1.0.bin",
                "version":"1.0.0.1",
                "size":125684,
                "md5":"jhfkansdfkemnfkwefnk",
                "date":"1442477272000",      # timestamp in microsecond
                "download":http://update.epic.com/update/downloads/id/11505,
                "explainEn":"it’s for update",
                "explainZh":"本次更新主要包含"
            },
            {
                …
            }
        ]

    '''
    print Useage

def return_value(errCode, errMsg, data):
    ret = {"errCode":errCode, "errMsg":str(errMsg), "data":data}
    return ret

class UpgradeManager():
    def __init__(self, site, proxyConf, devType, versionList):
        self.m_site = site if not site.startswith('http://') else site[7:]
        self.m_proxyConf = self.parse_proxy(proxyConf)
        self.m_devType = devType.upper()
        self.m_versionDict = self.parse_version_list(versionList)
        self.m_upgradeInfo = {}
        self.m_upgradeList = []
        if self.m_devType != "SAS":
            raise Exception('The device type should be SAS.')

    def __str__(self):
        return "site: %s\nproxy: %s\ndevType: %s\nversionList: %s\n" %\
               (self.m_site, self.m_proxyConf, self.m_devType, self.m_versionDict)

    def version2tuple(self, version_str):
        p = re.compile('(\d+)\D+(\d+)\D(\d+)\D*(\d*)\D*(\d*)\D*')
        m = re.search(p, version_str)
        if m is not None:
            return m.groups()
        else:
            return ('0','0','0','0','0')

    def new_version_is_greater(self, oldversion, newversion):
        if oldversion == "":
            return False
        old = self.version2tuple(oldversion)
        new = self.version2tuple(newversion)
        for i in range(0, len(old)):
            old_num = int(old[i]) if old[i] != '' else 0
            new_num = int(new[i]) if new[i] != '' else 0
            if old_num < new_num:
                return True
        return False

    def get_tags(self):
        p = re.compile('(\d+)\D+(\d+)\D+(\d+)\D*')
        m = re.search(p, self.m_versionDict['agent'])
        if m is not None:
            agent_tag = 'sas_' + '.'.join([str(int(item)) for item in m.groups()])
        else:
            raise Exception('Failed to get agent tag.')
        version = {'agent':agent_tag, 'urllib':'newurllib'}
        return version

    def parse_proxy(self, proxyString):
        # --proxyConf   the proxy config, eg:{"pswd": "pswd", "user": "user","server": "1.1.1.1","port":806}
        ret = {"pswd":"", "user":"", "server":"", "port":""}
        if proxyString == "":
            return ret
        proxy_conf = json.loads(proxyString)
        get_value = lambda k, D: str(D[k]) if k in D else ""
        ret["pswd"] = get_value("pswd", proxy_conf)
        ret["user"] = get_value("user", proxy_conf)
        ret["server"] = get_value("server", proxy_conf)
        ret["port"] = get_value("port", proxy_conf)
        return ret

    def parse_version_list(self, verString):
        '''
        --versionList the device's all version info, eg:
        [
            {"vtype":"firmware","version":"5.6.9.31"},
            {"vtype":"agent","version":"5.6.9.63"},
            {"vtype":"sysrule","version":"5.6.9.12581"},
            {"vtype":"urllib","version":"5.6.0.130"},
            {"vtype":"avrule","version":"5.6.9.12542"}
        ]
        '''
        ret = {"firmware":"", "agent":"", "sysrule":"", "urllib":"", "avrule":""}
        if verString == "":
            return ret
        ver_list = json.loads(verString)
        for item in ver_list:
            vtype = item["vtype"]
            version = item["version"]
            if vtype in ret:
                ret[vtype] = version
        return ret

    def parse_upgrade_file(self, fp):
        root = ElementTree.parse(fp).getroot()
        tags = self.get_tags()
        for name, tag in tags.viewitems():
            node = root.find(tag)
            if node is not None:
                self.m_upgradeInfo[name] = node.attrib
            else:
                self.m_upgradeInfo[name] = {}

    def need_upgrade(self, name):
        if not self.m_upgradeInfo.has_key(name):
            return False
        for vtype, version in self.m_versionDict.viewitems():
            if vtype == name:
                new_version = self.m_upgradeInfo[name]['version']
                if self.new_version_is_greater(version, new_version):
                    return True
                else:
                    return False
        return False

    def get_upgrade_list(self):
        for name, value in self.m_upgradeInfo.viewitems():
            if not value.has_key('version'):
                continue
            if self.need_upgrade(name):
                data = {'devType':'SAS'}
                data['patchType'] = name
                data['name'] = value['filename']
                data['version'] = value['version']
                data['size'] = int(value['size'])
                data['md5'] = value['md5']
                data['date'] = int(1000 * (time.mktime(time.strptime(value['date'], "%Y-%m-%d %H:%M:%S"))))
                data['download'] = value['url']
                data['explainEn'] = value['description_en_US']
                data['explainZh'] = value['description_zh_CN']
                self.m_upgradeList.append(data)
        return self.m_upgradeList

    def get_upgrade_info(self):
        url = "http://%s/%s" % (self.m_site, 'update/listLatest')
        opener = urllib2.build_opener()
        if self.m_proxyConf["server"] != "":
            proxy_port = 80
            if self.m_proxyConf["port"] != "":
                proxy_port = self.m_proxyConf["port"]
            proxy_url = "http://%s:%s/" % (self.m_proxyConf["server"], proxy_port)
            proxy_handler = urllib2.ProxyHandler({'http':proxy_url})
            opener.add_handler(proxy_handler)

            # process authentication
            username = self.m_proxyConf["user"]
            password = self.m_proxyConf["pswd"]
            if username != "":
                proxy_auth_handler = urllib2.ProxyBasicAuthHandler()
                proxy_auth_handler.add_password('realm', 'host', username, password)
                opener.add_handler(proxy_auth_handler)
        data = []
        try:
            fp = opener.open(url, timeout=60)
            if fp.getcode() != 200:
                return(BADSTATUS, "Response status code is %d" % fp.getcode(), [])
            self.parse_upgrade_file(fp)
            data = self.get_upgrade_list()
        except urllib2.HTTPError, err:
            return return_value(HTTPERROR, err, [])
        except urllib2.URLError, err:
            return return_value(URLERROR, err, [])
        except Exception, err:
            traceback.print_exc()
            return return_value(EXCEPTION, err, [])
        return return_value(0, 'Success', data)

def main():
    try:
        expected_opts = [SITE, PROXY, DEVTYPE, VERSIONLIST]
        option, value = getopt.getopt(sys.argv[1:], '', ['help', 'site=', 'proxyConf=', 'devType=', 'versionList='])
        params = dict(option)
        if '--help' in params:
            usage()
            exit(0)
        if len(option) != len(expected_opts):
            raise BaseException()
        up_mgr = UpgradeManager(params[SITE], params[PROXY], params[DEVTYPE], params[VERSIONLIST])
        ret = up_mgr.get_upgrade_info()
        #print json.dumps(ret, indent=4)
        print json.dumps(ret)
    except SystemExit:
        pass
    except:
        traceback.print_exc()
        #usage()
        ret = return_value(EXCEPTION, str(traceback.format_exc()), [])
        #print json.dumps(ret, indent=4)
        print json.dumps(ret)
def test_case():
    up_mgr = UpgradeManager(
        'http://update.epic.com/update/listLatest/', 
        '{"pswd": "pswd", "user": "user","server": "","port":0}', 
        'SAS', 
        """
        [
            {"vtype": "firmware", "version": "V2.0R01F00SP00"},
            {"vtype": "agent",    "version": "V5.6R8F00B52617"},
            {"vtype": "build",    "version": "48647"}
        ]
        """)
    ret = up_mgr.get_upgrade_info()
    print json.dumps(ret, indent=4)

if __name__ == '__main__':
    main()
    #test_case()
